"use strict";
var delta = 0.0000000001;

//Just various ascii values for the various keys. Used for the desktop.
var left_arrow = 37;
var up_arrow = 38;
var right_arrow = 39;
var down_arrow = 40;
var space = 32;
var pressed_keys = {};

var autofire = false;
var shoot_speed = 1100 / 5;
var enemy_shoot_speed = shoot_speed * 2;
var circle_speed = 6;
var bullet_speed = 5;
var enemy_speed = 4;

var fade_time = 20;
var hit_cooldown = 200;
var fps = 60;
var test_fps = 0.5;
var normal_speed = 1000 / fps;
var slow_speed = 1000 / test_fps;
var default_font_size = 15;
var default_font = default_font_size + "pt Verdana";

var bar_width = 10;

var left_buffer = 50;
var right_buffer = 50;
var top_buffer = 100;
var bottom_buffer = 0;

var canvas_id = "c";
var canvas = document.getElementById(canvas_id);

var debug_color = "#00FFFF";
//This HAS to be in RGB format or IE breaks the transparency
var circle_color = "255, 0, 0";
//A default color used for the player's ship.
//Not actually used currently, as the ship overrides the draw method
//to draw the kidney image instead.
var ship_color = "#FF0000";
var bg_color = "#000000";
var blue = "#0000FF";
var light_blue = "#00a2e8";
var white = "#FFFFFF";
var black = "#000000";

var howManyCircles = 10;
var max_circle_radius = 100;
var bullet_power = 1;
//This shows how long the mouse has been held down (TODO)
var charge_timer;
var charged_multiplier = 2;
var max_medicine = 500;
var max_health = 10;
var bar_height = 80;
var health_bar_color = "#FF0000";
var medicine_bar_color = "#00FF00";
var bar_background_color = "#DDDDDD";

var circle_layer = 0;
var enemy_layer = 1;
var player_layer = 2;
var explosion_layer = 3;
var buffer_layer = 4;
var text_layer = 5;
var bar_layer = 6;

//Kept to be readded at a later date.
/**    var dx = 0;
    var dy = 0;
    if (pressed_keys[left_arrow]) {
        log("LEFT");
        dx = dx - speed;
    }
    if (pressed_keys[right_arrow]) {
        log("RIGHT");
        dx = dx + speed;
    }
    if (pressed_keys[up_arrow]) {
        log("UP");
        dy = dy - speed;
    }
    if (pressed_keys[down_arrow]) {
        log("DOWN");
        dy = dy + speed;
    }
    this.x = this.x + dx;
    this.y = this.y + dy;**/

//What this is doing is setting the name attribute
//on all classes, so we can easily get an instance's class name
if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
    Object.defineProperty(Function.prototype, 'name', {
        get: function () {
            var funcNameRegex = /function\s([^(]{1,})\(/;
            var results = funcNameRegex.exec(this.toString());
            return (results && results.length > 1) ? results[1].trim() : "";
        },
        set: function(value) {}
    });
}

window.requestAnimationFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          function( callback ){
            window.setTimeout(callback, normal_speed);
          };
}());


function setup() {
    var canvas = document.getElementById(canvas_id);
	
    if (window.innerWidth > 900) {
        canvas.width = 320;
        canvas.height = 500;
    }
    else {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    canvas.left = left_buffer;
    canvas.right = canvas.width - right_buffer;
    canvas.top = top_buffer;
    canvas.bottom = canvas.height - bottom_buffer;
    canvas.middle_x = (canvas.right - canvas.left)/2;
    canvas.middle_y = (canvas.bottom - canvas.top)/2;
    canvas.style.position = "absolute";
    canvas.style.left = (window.innerWidth/2 - canvas.width/2) + "px";
    var i = 0;
    canvas.layers = {};
    canvas.waiting_enemies = 0;
    draw_rect(0, 0, canvas.width, canvas.height, canvas, bg_color, true);
    canvas.layers[circle_layer] = [];
    for (i = 0; i < howManyCircles; i = i + 1) {
        var x = canvas.left + Math.random() * (canvas.right - canvas.left);
        var y = canvas.top + Math.random() * (canvas.bottom - canvas.top);
        var circle = new Circle(x, y, 0, circle_speed, Math.random() * max_circle_radius, Math.random() / 2, canvas);
        canvas.layers[circle_layer].push(circle);
    }
    canvas.player = new Kidney(canvas.middle_x, (3 / 4) * (canvas.bottom - canvas.top), canvas);
    canvas.layers[player_layer] = [canvas.player];
    canvas.layers[explosion_layer] = [];
    
    var left_rect = new Rect(0, 0, canvas.left, canvas.height, bg_color, canvas);
    var right_rect = new Rect(canvas.right, 0, canvas.width - canvas.right, canvas.height, bg_color, canvas);
    var medicine_rect_bg = new Rect(canvas.left, 5, canvas.right - canvas.left - 10, canvas.top - 5, white, canvas);
    var medicine_rect = new Rect(canvas.left + 5, 10, canvas.right - canvas.left - 20, canvas.top-15, light_blue, canvas);
    
    canvas.layers[buffer_layer] = [left_rect, right_rect, medicine_rect_bg, medicine_rect];
    
    //Text(x,y, text, font, color, screen)
    var label_buffer = 3;
    var current_medicine_label = new Text(medicine_rect.x + 2, medicine_rect.y + label_buffer, "Medicine: ", default_font, black, canvas);
    var current_medicine_value = new Label(current_medicine_label.x + default_font_size * "Medicine: ".length + 3, current_medicine_label.y, window, "innerWidth", default_font, white, canvas);
    var dosage_label = new Text(medicine_rect.x + 2, current_medicine_label.y + default_font_size + label_buffer, "Dosage: ", default_font, black, canvas);
    var current_medicine_needed = new Label(current_medicine_label.x + default_font_size * "Dosage: ".length + 3, dosage_label.y, canvas.player, "medicine_level", default_font, white, canvas);
    var time_label = new Text(medicine_rect.x + 2, dosage_label.y + default_font_size + label_buffer, "Time: ", default_font, black, canvas);
    var time_value = new Label(time_label.x + default_font_size * "Time: ".length + 3, time_label.y, canvas.player, "medicine_level", default_font, white, canvas);
    canvas.layers[text_layer] = [current_medicine_label, dosage_label, time_label, current_medicine_value, current_medicine_needed, time_value];
    
    //Bar(x, y, target, attribute, y_scale, min, max, canvas)
    var health_bar = new Bar(canvas.width -3*bar_width -5, bar_height + 5, canvas.player, "health", bar_height/max_health, 0, max_health, health_bar_color, canvas);
    var medicine_bar = new Bar(canvas.width - bar_width - 5, bar_height + 5,canvas.player, "medicine_level", bar_height/max_medicine, 0, max_medicine, medicine_bar_color, canvas);
    canvas.layers[bar_layer] = [health_bar, medicine_bar];
   
    //Warning: This looks really complicated for two reasons:
    // 1) I'm using way more features than would be actually needed for testing purposes
    // 2) There are a lot of options involved, so it's genuinely complicated. (Not ridiculously so, but there's a lot to specify)
    
    //This is where the enemies are created. Each function returns a function that can be called to return a set of enemies to be placed at each enemy tick.
    //Syntax:
    //transformation(...(operation(placement_function(Flyer_Type, dx_function, dy_function, min_x, max_x, flyer_count), ...)))
    //For details on the various placement and dx/dy functions, please see wave.js
    
    //Explanation for the last wave:
    //placement_function    enemy_random(                           - Place the enemies randomly
    //enemy_class           Enemy,                                  - Use the normal enemy class  (make them normal enemies)
    //movement_function     zero(),                                 - Give them 0 movement along the x-axis
    //movement_function     constant(enemy_speed),                  - Move them enemy_speed units along the +y-axis at each tick
    //left                  0,                                      - Place them starting at x=0,
    //right                 canvas.width - Enemy.prototype.width,   - and stopping at that. (It ensures the enemy all fit in the screen)
    //count                 15,                                     - Place 15 of them.
    //canvas                canvas)                                 - Finally, draw them on the canvas with the desired id.
    
    //This layer is reserved for enemies
    canvas.layers[enemy_layer] = [];
    var test_waves = [combine(
                        seperate(enemy_sin(Pill, zero(), constant(enemy_speed), canvas.left, canvas.right - Pill.prototype.width, 12, 60, canvas), 1280),
                        
                        delayed(
                            combine(
                                enemy_checkerboard(Enemy, random(-3, 3), random(-1, 3), canvas.left, canvas.right - Enemy.prototype.width, 25, canvas), 
                                enemy_sin(StrongEnemy, loop([constant(-2), zero(), constant(2), zero()], 1000), constant(enemy_speed), canvas.left, canvas.middle_x - StrongEnemy.prototype.width, 10, 30, canvas)
                            ), 
                        50), 
                        translate(rotate(enemy_rows(StrongEnemy, zero(), constant(enemy_speed), canvas.left, canvas.right - StrongEnemy.prototype.width, 25, canvas), 45), StrongEnemy.prototype.width, -StrongEnemy.prototype.height), 
                        translate(enemy_sin(Enemy, zero(), constant(enemy_speed), canvas.left, canvas.right - Enemy.prototype.width, 12, 25, canvas), 10, 0), 
                        enemy_random(Enemy, zero(), constant(enemy_speed), canvas.left, canvas.right - Enemy.prototype.width, 15, canvas)
                    )
                   ];
	var real_waves = [enemy_sin(StrongEnemy, loop([constant(-2), zero(), constant(2), zero()], 1000), constant(enemy_speed), canvas.left, canvas.right, 10, 30, canvas)];
	canvas.waves = real_waves;
    canvas.update_enemies = get_enemies();
}

/** Explanation for the class hierarchy:

Thing is the root of the hierarchy, essentially a graph-oriented version of the 
standard Object class.

Directly inheriting from him are the Flyer and Bullet classes. The bullet class
is fairly straightforward. 

The Flyer class represents sentient objects that actively move. (As opposed to the 
bullet's passive motion)

Subclassing Flyer are Enemy and Kidney. Kidney is also fairly straightforward. 

Enemy represents exactly what it sounds like. It will probably ultimately have various 
subclasses, depending on our creativity.
**/
function Thing(x, y, screen) {
    this.x = x;
    this.y = y;
    //Certain movement functions use parametric definitions (i.e. moving along a curve (Thing.set_path))
    this.t = 0;
    //We don't use the cool functions used in canvas.waves as at the time this constructor is called, wave.js might not have been imported yet.
    //None of the stuff defined in other functions can be used without being careful about it in the various constructors.
    this.dx = function () {return 0;};
    this.dy = function () {return enemy_speed;};
    this.dt = function () {return 1;};
    this.width = 10;
    this.height = 10;
    //The canvas the thing belongs to
    this.screen = screen;
    this.color = debug_color;
    //When it's dead, it's removed from the canvas' object array so it can be garbage collected.
    this.dead = false;
    //Whether the thing has ever been inside the bounds of the canvas or not.
    this.seen = false;
    //The number of iterations of the main loop in which the thing was outside the bounds of the canvas.
    this.ticks_unseen = 0;
    //I hate that we have to explicitly set this. I would like to
    //find some automatic solution.
    this.frameCount = 1;
    this.last_hit = [];
    //Distinguishes the item as being from an enemy or the player
    this.team = "";
}
Thing.prototype = new Object();
Thing.prototype.constructor = Thing;

Thing.prototype.max_ticks_unseen = 500;
//Garbage values assigned just in case someone forgets to set them for a subclass
Thing.prototype.height = 10;
Thing.prototype.width = 10;

Thing.prototype.draw = function () {
    draw_rect(this.x, this.y, this.width, this.height, this.screen, this.color, true);
};

Thing.prototype.move = function () {
    var dt = this.dt();
    var dy = this.dy();
    this.x = this.x + this.dx()*dt;
    this.y = this.y + dy*dt;
};
Thing.prototype.update = function () {
    this.move();
    this.check_visibility();
};

Thing.prototype.check_collision = function () {
    var hit = [];
    var i = 0;
    var j = 0;
    
    var left1 = this.x;
    var top1 = this.y;
    var width = this.width;
    var height = this.height;
    var right1 = left1 + width;
    var bottom1 = top1 + height;
    for (i = 0; i < Object.getOwnPropertyNames(this.screen.layers).length; i = i + 1) {
        var layer = this.screen.layers[i];
        for (j = 0; j < layer.length; j = j + 1) {
            var obj = layer[j];
            if (!(obj === this)) { 
                var left2 = obj.x;
                var top2 = obj.y;
                var right2 = left2 + obj.width;
                var bottom2 = top2 + obj.height;
                if (bottom1 <= top2 || top1 >= bottom2 || right1 <= left2 || left1 >= right2) {
                    continue;
                }
                else {
                    hit.push(obj);
                }
            }
        }
    }
    var self = this;
    var new_hit = false;
    if (this.last_hit.length === 0) {
        if (hit.length > 0) {
            for (i = 0; i < hit.length; i = i + 1) {
                if (this.last_hit.indexOf(hit[i]) === -1) {
                    new_hit = true;
                    break;
                }
            }
            if (new_hit) {
                this.last_hit = hit;
                setTimeout(function () {self.last_hit = [];}, hit_cooldown);
            }
            else {
                hit = [];
            }
        }
    }
    else {
        hit = [];
    }
    return hit;
};
Thing.prototype.handle_collision = function (hit_thing) {
    return;
};

/** This moves the thing along the path starting with (x0, y0) and ending at (x2, y2).
(x1, y1) is a control point, meaning that the thing might not actually even touch it.
This is just a quadratic bezier curve.**/

/** Checks to see if the thing is in the borders of the canvas. If it remains outside the borders for too long, 
it is considered dead and is garbage collected.

This function also manages the seen attribute, which indicates whether the thing has ever been
inside the borders of the canvas. This is only used by the various enemy classes. (Once all of the
enemies in the current wave have been seen, the next wave can be sent out)**/
Thing.prototype.check_visibility = function () {
    var is_seen = (this.y < this.screen.bottom && this.y > this.screen.top) && 
    (this.x > this.screen.left && this.x < this.screen.right);
    if (!is_seen) {
        this.ticks_unseen = this.ticks_unseen + 1;
        if (this.ticks_unseen > this.max_ticks_unseen) {
            this.dead = true;
        }
    }
    else {
        this.seen = true;
    }
};

function Rect(x, y, width, height, color, screen) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.color = color;
    this.screen = screen;
}
Rect.prototype = new Thing(0, 0);
Rect.prototype.constructor = Rect;

Rect.prototype.update = function () {
    return;
};

Rect.prototype.check_collision = function () {
    return [];
};

function Bar(x, y, target, attribute_name, y_scale, min_value, max_value, color, screen) {
    
    this.target = target;
    this.attribute_name = attribute_name;
    this.level = this.target[this.attribute_name];
    this.screen = screen;
    this.x = x;
    this.y = y;
    this.width = bar_width;
    this.seen = true;
    this.min_value = min_value;
    this.max_value = max_value;
    this.y_scale = y_scale;
    this.height = (this.max_value - this.min_value) * this.y_scale;
    this.color = color;
}

Bar.prototype = new Thing(0, 0);
Bar.prototype.constructor = Bar;

Bar.prototype.max_ticks_unseen = Number.POSITIVE_INFINITY;

Bar.prototype.draw = function () {
    draw_rect(this.x, this.y, this.width, -this.height, this.screen, "#FFFFFF", true);
    draw_rect(this.x, this.y, this.width,-this.y_scale*Math.max(this.min_value, Math.min(this.level, this.max_value)), this.screen, this.color, true);
};

Bar.prototype.move = function () {
    return;
};
Bar.prototype.update = function () {
    this.level = this.target[this.attribute_name];
};

Bar.prototype.check_collision = function () {
    return [];
};

function Text(x,y, text, font, color, screen) {
    this.text = text;
    this.x = x;
    this.y = y;
    this.seen = true;
    this.font = font;
    this.color = color;
    this.screen = screen;
}

Text.prototype = new Thing(0,0);
Text.prototype.contructor = Text;

Text.prototype.max_ticks_unseen = Number.POSITIVE_INFINITY;

Text.prototype.draw = function () {
    draw_text(this.x, this.y, this.text, this.screen, this.font, this.color, true, 1);
};
Text.prototype.move = function() {
    return;
};

Text.prototype.update = function () {
    return;
};

Text.prototype.check_collision = function () {
    return [];
};

/**Represents text that modifies based on some attribute of an object.**/
function Label(x, y, target, attribute_name, font, color, screen) {
    this.x = x;
    this.y = y;
    this.seen = true;
    this.font = font;
    this.color = color;
    this.screen = screen;
    this.target = target;
    this.attribute_name = attribute_name;
    this.text = this.target[this.attribute_name];
}

Label.prototype = new Text(0,0, "", default_font, white);
Label.prototype.contructor = Label;

Label.prototype.update = function () {
    this.text = this.target[this.attribute_name];
};

function Bullet(x, y, dx, dy, power, team, screen) {
    this.x = x;
    this.y = y - this.height;
    this.screen = screen;
    this.dx = function () {return dx;};
    this.dy = function () {return dy;};
    //Power is not yet implemented. Will ultimately control
    //how much damage it does to its target
    this.power = power;
    //Who fired the bullet
    this.team = team;
}
Bullet.prototype = new Thing(0, 0); 
Bullet.prototype.constructor = Bullet;    

function Explosion(x, y, screen) {
    this.x = x;
    this.y = y;
    this.screen = screen;
    var self = this;
    this.dy = function () {return 0;};
    if (!(screen === undefined)) {
        load_image("explosion.png", self);//, fade);
    }
}
Explosion.prototype = new Thing(0,0);
Explosion.prototype.constructor = Explosion;
Explosion.prototype.draw = function () {
    var graph = this.screen.getContext("2d");
    try {
        //drawImage(image, x clip start, y clip start, clip width, clip height, x, y, width, height)
        graph.globalAlpha=this.image.style.opacity;
        graph.drawImage(this.image, this.x, this.y, this.image.width, this.image.height);
        graph.globalAlpha=1;
    }
    catch (e) {
    }
};
Explosion.prototype.update = function() {
    Thing.prototype.update.call(this);
    if (Math.abs($(this.image).css("opacity")) < delta) {
        this.dead = true;
    }
    else {
        fadeOut($(this.image), fade_time);
    }
};

function Pill(x, y, screen) {
    this.x = x;
    this.y = y;
    this.screen = screen;
    var self = this;
    if (!(screen === undefined)) {
        load_image("prograf.png", self);
    }
    this.value = 1;
    
}
Pill.prototype = new Thing(0,0);
Pill.prototype.constructor = Pill;
//I hate setting these manually. It's fragile and awkward.
Pill.prototype.height = 27;
Pill.prototype.width = 11;
Pill.prototype.max_ticks_unseen = Number.POSITIVE_INFINITY;

Pill.prototype.draw = function () {
    var graph = this.screen.getContext("2d");
    try {
        //drawImage(image, x clip start, y clip start, clip width, clip height, x, y, width, height)
        graph.drawImage(this.image, this.x, this.y, this.image.width, this.image.height);
    }
    catch (e) {
    }
};


function Circle(x, y, dx, dy, radius, opacity, screen) {
    this.x = x;
    this.y = y;
    this.dx = function () {return dx;};
    this.dy = function () {return dy;};
    this.screen = screen;
    this.radius = radius;
    this.filled = true;
    this.opacity = opacity;
    this.color = circle_color;
}
Circle.prototype = new Thing(0, 0);
Circle.prototype.constructor = Circle;
Circle.prototype.max_ticks_unseen = Number.POSITIVE_INFINITY;

Circle.prototype.move = function () {
    this.x = this.x + this.dx();
    this.y = this.y + this.dy();
    if (this.y - this.radius > this.screen.bottom) {
        this.x = canvas.left + Math.random() * (canvas.right - canvas.left);
        this.radius = Math.random() * max_circle_radius;
        this.y = -this.radius;
        this.opacity = Math.random() / 2;
    }
};

Circle.prototype.draw = function() {
    draw_circle(this.x, this.y, this.radius, this.screen, this.color, this.filled, this.opacity);
};

function Flyer(x, y, screen) {
    this.x = x;
    this.y = y;
    this.screen = screen;
    this.charged = false;
    this.currentFrame = 0;
    this.currentTick = 0;
    this.seen = false;
    this.tickPerFrame = 4;
}
Flyer.prototype = new Thing(0,0);        // Here's where the inheritance occurs
Flyer.prototype.constructor = Flyer;       // Otherwise instances of Cat would have a constructor of Mammal

Flyer.prototype.draw = function () {
    draw_triangle(this.x, this.y, this.width, this.height, this.screen, ship_color, false);
};
Flyer.prototype.move = function () {
    return;
};
Flyer.prototype.update = function () {
    var i = 0;
    this.move();
    this.check_visibility();
    var hit = this.check_collision();
    for (i = 0; i < hit.length; i = i + 1) {
        this.handle_collision(hit[i]);
    }
};

Flyer.prototype.handle_collision = function (hit_thing) {
    if (hit_thing.constructor.name === "Bullet" && hit_thing.team !== this.team) {
            hit_thing.dead = true;
            this.dead = true;
            var explosion = new Explosion(this.x, this.y, this.screen);
            this.screen.layers[explosion_layer].push(explosion);
    }
};
/**
Flyer.prototype.shoot = function () {
    if ((this.y > this.screen.top && this.y < this.screen.bottom) && (this.x > this.screen.left && this.x < this.screen.right)) {
        var power = bullet_power;
        if (this.charged) {
            power = power * charged_multiplier;
        }
        var self = this;
        var dt = this.dt();
        log(dt, this.dx(), this.dy());
        var bullet = new Bullet(this.x + this.dx()*dt*2, this.y + this.dy()*dt*2, 0, -bullet_speed, power, self, this.screen);
        this.screen.layers[enemy_layer].push(bullet);
        this.charged = false;
    }
};**/
Flyer.prototype.shoot = function () {
    if (!this.dead) {
        if ((this.y > this.screen.top && this.y < this.screen.bottom) && (this.x > this.screen.left && this.x < this.screen.right)) {
            var power = bullet_power;
            if (this.charged) {
                power = power * charged_multiplier;
            }
            var direction = 1;
            var dy = this.dy();
            if (dy > 0) {
                direction = 1;
            }
            else {
                direction = -1;
            }
            var offset = 0;
            if (this.constructor.name !== "Kidney") {
                offset = this.height * 1.5;
            }
            var bullet = new Bullet(this.x, this.y + direction * offset, 0, bullet_speed * direction, power, this.team, this.screen);
            this.screen.layers[enemy_layer].push(bullet);
            this.charged = false;
        }
    }
    else {
        clearTimeout(this.shoot_timer);
    }
};

function Kidney(x, y, screen) {
    this.x = x;
    this.y = y;
    this.dy = function () {return -enemy_speed;};
    this.screen = screen;
    this.frameCount = 2;
    if (!(screen === undefined)) {
        load_image("kidneyCape2.png", this);
    }
    this.currentFrame = 0;
    this.health = max_health;
    this.medicine_level = max_medicine;
    if (autofire && !(screen === undefined)) {
        var me = this;
        var q = function() {
            me.shoot();
        };
        setInterval(q, shoot_speed);
    }
    this.team = "player";
}
Kidney.prototype = new Flyer(0, 0);        // Here's where the inheritance occurs
Kidney.prototype.constructor = Kidney;

Kidney.prototype.update = function () {
    Flyer.prototype.update.call(this);
    var i = 0;
    this.check_visibility();
    var hit = this.check_collision();
    for (i = 0; i < hit.length; i = i + 1) {
        this.handle_collision(hit[i]);
    }
    this.medicine_level = this.medicine_level - 1;
};

Kidney.prototype.setPosition = function (x, y) {
    this.x = x;
    this.y = y;
};

Kidney.prototype.draw = function () {
    var graph = this.screen.getContext("2d");
    try {
        //drawImage(image, x clip start, y clip start, clip width, clip height, x, y, width, height)
        graph.drawImage(this.image, 0, this.height * this.currentFrame, this.width, this.height, this.x, this.y, this.width, this.height);
    }
    catch (e) {
    }
    //What this does is ticks the sprite's animation every tickPerFrame ticks
    if (this.currentTick === this.tickPerFrame) {
        if (this.currentFrame === (this.frameCount - 1)) {
            this.currentFrame = 0;
        }
        else {
            this.currentFrame = this.currentFrame + 1;
        }
        this.currentTick = 0;
    }
    this.currentTick = this.currentTick + 1;
};

Kidney.prototype.handle_collision = function (hit_thing) {
    if ((hit_thing.constructor.name === "Bullet" && !(hit_thing.team === this.team)) || hit_thing.constructor.name.indexOf("Enemy") !== -1) {
        this.health = this.health - 1;
        var explosion = new Explosion(this.x, this.y, this.screen);
        this.screen.layers[explosion_layer].push(explosion);
        
    }
    else if (hit_thing.constructor.name === "Pill") {
        this.medicine_level = this.medicine_level + hit_thing.value;
    }
};

function Enemy(x, y, screen) {
    this.x = x;
    this.y = y;
    this.screen = screen;
    //Here we exploit the fact that we consciously
    //let screen be undefined when setting up prototypes for
    //inheritance. After setting up the class system,
    //we wait until all other scripts are imported, so 
    //any other calls to their various functions are safe.
    if (!(screen === undefined)) {
        load_image("badGuy.png", this);
    }
    this.currentFrame = 0;
    this.tickPerFrame = 8;
    if (!(screen === undefined)) {
        screen.waiting_enemies = screen.waiting_enemies + 1;
    }
    this.health = 10;
    
    if (!(screen === undefined)) {
        var me = this;
        var q = function() {
            me.shoot();
        };
        this.shoot_timer = setInterval(q, enemy_shoot_speed);
    }
    this.team = "enemy";
}
Enemy.prototype = new Flyer(0, 0);        // Here's where the inheritance occurs
Enemy.prototype.constructor = Enemy;
//I hate setting these manually. It's fragile and awkward.
Enemy.prototype.height = 44;
Enemy.prototype.width = 33;

Enemy.prototype.move = function () {
    var dt = this.dt();
    this.t = this.t + dt;
    this.y = this.y + this.dy()*dt;
    this.x = this.x + this.dx()*dt;
    
    var was_seen = this.seen;
    this.check_visibility();
    //If it's been seen for the first time, consider it forever seen and let the next wave come out
    if (this.seen && (was_seen === false)) {
        this.screen.waiting_enemies = this.screen.waiting_enemies - 1;
    }
    else if (this.dead) {
        this.screen.waiting_enemies = this.screen.waiting_enemies - 1;
    }
};

Enemy.prototype.draw = function () { 
    var graph = this.screen.getContext("2d");
    try {
        //drawImage(image, x clip start, y clip start, clip width, clip height, x, y, width, height)
        graph.drawImage(this.image, 0, this.height * this.currentFrame, this.width, this.height, this.x, this.y, this.width, this.height);
    }
    catch (e) {
    }
    //What this does is ticks the sprite's animation every tickPerFrame ticks
    if (this.currentTick === this.tickPerFrame) {
        if (this.currentFrame === (this.frameCount - 1)) {
            this.currentFrame = 0;
        }
        else {
            this.currentFrame = this.currentFrame + 1;
        }
        this.currentTick = 0;
    }
    this.currentTick = this.currentTick + 1;
};        

function StrongEnemy(x, y, screen) {
    this.x = x;
    this.y = y;
    this.screen = screen;
    //I'm not sure I like setting default health values this way either.
    this.health = 20;
    this.frameCount = 2;
    if (!(screen === undefined)) {
        load_image("enemy.png", this);
    }
    this.screen.waiting_enemies = this.screen.waiting_enemies + 1;
    if (!(screen === undefined)) {
        var me = this;
        var q = function() {
            me.shoot();
        };
        setInterval(q, enemy_shoot_speed);
    }
    this.team = "enemy";
}
StrongEnemy.prototype = new Enemy(0, 0);        // Here's where the inheritance occurs
StrongEnemy.prototype.constructor = StrongEnemy;
StrongEnemy.prototype.height = 54;
StrongEnemy.prototype.width = 33;

function get_enemies() {
    var i = 0;
    return function () {
        if ((canvas.waves.length > 0)) {
            var enemies = canvas.waves[0]();
            if (enemies) {
                for (i =0; i < enemies.length; i = i + 1) {
                    canvas.layers[enemy_layer].push(enemies[i]);
                }
            }
            else {
                if (enemies === false && canvas.waiting_enemies === 0) {
                    canvas.waves.splice(0, 1);
                }
            }
        }
    };
}

function gameLoop() {
    var i = 0;
    var j = 0;
    var layer;
    var canvas = document.getElementById(canvas_id);
    draw_rect(0, 0, canvas.width, canvas.height, canvas, bg_color, true);
    draw_line(0, canvas.top, canvas.width, canvas.top, canvas, ship_color);
    draw_line(0, canvas.bottom, canvas.width, canvas.bottom, canvas, ship_color);
    draw_line(canvas.left, 0, canvas.left, canvas.height, canvas, ship_color);
    draw_line(canvas.right, 0, canvas.right, canvas.height, canvas, ship_color);
    canvas.update_enemies();
    
    //We iterate in reverse order so we can remove items without messing up the iteration
    for (i= 0; i < Object.getOwnPropertyNames(canvas.layers).length; i = i + 1) {  
        layer = canvas.layers[i];
        for (j = layer.length - 1; j >= 0; j = j - 1) {
            layer[j].update();
            if (layer[j].dead) {
                layer.splice(j, 1);
            }
        }
    }
    for (i= 0; i < Object.getOwnPropertyNames(canvas.layers).length; i = i + 1) {  
        layer = canvas.layers[i];
        for (j = 0; j < layer.length; j = j + 1) {
            layer[j].draw();
        }
    }
    requestAnimationFrame(gameLoop);
}

$(document).keydown(function(event) {
    var canvas = document.getElementById(canvas_id);
    var key = event.keyCode;
    pressed_keys[key] = true;
    if (key === space) { 
        var ship = canvas.player.shoot();
            
        log("SPACE");
        event.preventDefault();
    }
});

var mousemove = function(event) {
    event.preventDefault();
    var canvas = document.getElementById(canvas_id);
    if (canvas.player) {
        var mouseX = event.pageX - canvas.offsetLeft;
        var mouseY = event.pageY - canvas.offsetTop;
        canvas.player.setPosition(mouseX-canvas.player.width/2, mouseY);
    }
};
$(document).mousemove(mousemove);
document.addEventListener('touchmove', mousemove);

$(document).mousedown(function () {});
var mouseup = function (event) {
    event.preventDefault();
    if (canvas.player) {
        canvas.player.shoot();
        clearTimeout(charge_timer);
    }
};
$(document).mouseup(mouseup);
document.addEventListener('touchend', mouseup);
$(window).resize(function (event) {
    canvas.style.left = (window.innerWidth/2 - canvas.width/2) + "px";
    log("resized");
});

$.when(
    $.getScript("draw.js"),
    $.getScript("general.js"),
    $.getScript("wave.js"),
    $.Deferred(function (deferred) {
        $( deferred.resolve );
    })
).done(function () {setup(); gameLoop();});